From 2f27d292472d65060f43bd6d5a41885112099b21 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sun, 26 Jul 2020 17:55:54 +0200 Subject: [PATCH] Add GtkBoolFilter Takes a boolean GtkExpression (like a boolean object property) to run a filter with. --- docs/reference/gtk/gtk4-docs.xml | 1 + docs/reference/gtk/gtk4-sections.txt | 13 ++ gtk/gtk.h | 1 + gtk/gtkboolfilter.c | 295 +++++++++++++++++++++++++++ gtk/gtkboolfilter.h | 53 +++++ gtk/meson.build | 4 +- testsuite/gtk/filter.c | 45 ++++ 7 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 gtk/gtkboolfilter.c create mode 100644 gtk/gtkboolfilter.h diff --git a/docs/reference/gtk/gtk4-docs.xml b/docs/reference/gtk/gtk4-docs.xml index 8a10fe4908..37b22c4d80 100644 --- a/docs/reference/gtk/gtk4-docs.xml +++ b/docs/reference/gtk/gtk4-docs.xml @@ -55,6 +55,7 @@ + diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 2881f94dbe..5026765ca3 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -7571,6 +7571,19 @@ GTK_TYPE_EXPRESSION gtk_expression_get_type +
+gtkboolfilter +GtkBoolFilter +gtk_bool_filter_new +gtk_bool_filter_get_expression +gtk_bool_filter_set_expression +gtk_bool_filter_get_invert +gtk_bool_filter_set_invert + + +gtk_bool_filter_get_type +
+
gtkstringfilter GtkStringFilter diff --git a/gtk/gtk.h b/gtk/gtk.h index 50c23caf26..2277e1136d 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtkboolfilter.c b/gtk/gtkboolfilter.c new file mode 100644 index 0000000000..9a5ac17e1b --- /dev/null +++ b/gtk/gtkboolfilter.c @@ -0,0 +1,295 @@ +/* + * Copyright © 2020 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gtkboolfilter.h" + +#include "gtkintl.h" +#include "gtktypebuiltins.h" + +/** + * SECTION:gtkboolfilter + * @Title: GtkBoolFilter + * @Short_description: Filtering by boolean expressions + * + * GtkBoolFilter is a simple filter that takes a boolean #GtkExpression + * to determine whether to include items. + */ + +struct _GtkBoolFilter +{ + GtkFilter parent_instance; + + gboolean invert; + GtkExpression *expression; +}; + +enum { + PROP_0, + PROP_EXPRESSION, + PROP_INVERT, + NUM_PROPERTIES +}; + +G_DEFINE_TYPE (GtkBoolFilter, gtk_bool_filter, GTK_TYPE_FILTER) + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +static gboolean +gtk_bool_filter_match (GtkFilter *filter, + gpointer item) +{ + GtkBoolFilter *self = GTK_BOOL_FILTER (filter); + GValue value = G_VALUE_INIT; + gboolean result; + + if (self->expression == NULL || + !gtk_expression_evaluate (self->expression, item, &value)) + return FALSE; + result = g_value_get_boolean (&value); + + g_value_unset (&value); + + if (self->invert) + result = !result; + + return result; +} + +static GtkFilterMatch +gtk_bool_filter_get_strictness (GtkFilter *filter) +{ + GtkBoolFilter *self = GTK_BOOL_FILTER (filter); + + if (self->expression == NULL) + return GTK_FILTER_MATCH_NONE; + + return GTK_FILTER_MATCH_SOME; +} + +static void +gtk_bool_filter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkBoolFilter *self = GTK_BOOL_FILTER (object); + + switch (prop_id) + { + case PROP_EXPRESSION: + gtk_bool_filter_set_expression (self, gtk_value_get_expression (value)); + break; + + case PROP_INVERT: + gtk_bool_filter_set_invert (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_bool_filter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkBoolFilter *self = GTK_BOOL_FILTER (object); + + switch (prop_id) + { + case PROP_EXPRESSION: + gtk_value_set_expression (value, self->expression); + break; + + case PROP_INVERT: + g_value_set_boolean (value, self->invert); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_bool_filter_dispose (GObject *object) +{ + GtkBoolFilter *self = GTK_BOOL_FILTER (object); + + g_clear_pointer (&self->expression, gtk_expression_unref); + + G_OBJECT_CLASS (gtk_bool_filter_parent_class)->dispose (object); +} + +static void +gtk_bool_filter_class_init (GtkBoolFilterClass *class) +{ + GtkFilterClass *filter_class = GTK_FILTER_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + filter_class->match = gtk_bool_filter_match; + filter_class->get_strictness = gtk_bool_filter_get_strictness; + + object_class->get_property = gtk_bool_filter_get_property; + object_class->set_property = gtk_bool_filter_set_property; + object_class->dispose = gtk_bool_filter_dispose; + + /** + * GtkBoolFilter:expression: (type GtkExpression) + * + * The boolean expression to evalute on item + */ + properties[PROP_EXPRESSION] = + gtk_param_spec_expression ("expression", + P_("Expression"), + P_("Expression to evaluate"), + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * GtkBoolFilter:invert: + * + * If the expression result should be inverted + */ + properties[PROP_INVERT] = + g_param_spec_boolean ("invert", + P_("Invert"), + P_("If the expression result should be inverted"), + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); +} + +static void +gtk_bool_filter_init (GtkBoolFilter *self) +{ +} + +/** + * gtk_bool_filter_new: + * @expression: (transfer full) (nullable): The expression to evaluate + * or %NULL for none + * + * Creates a new bool filter. + * + * Returns: a new #GtkBoolFilter + **/ +GtkFilter * +gtk_bool_filter_new (GtkExpression *expression) +{ + GtkFilter *result; + + result = g_object_new (GTK_TYPE_BOOL_FILTER, + "expression", expression, + NULL); + + g_clear_pointer (&expression, gtk_expression_unref); + + return result; +} + +/** + * gtk_bool_filter_get_expression: + * @self: a #GtkBoolFilter + * + * Gets the expression that the filter uses to evaluate if + * an item should be filtered. + * + * Returns: (transfer none): a #GtkExpression + */ +GtkExpression * +gtk_bool_filter_get_expression (GtkBoolFilter *self) +{ + g_return_val_if_fail (GTK_IS_BOOL_FILTER (self), NULL); + + return self->expression; +} + +/** + * gtk_bool_filter_set_expression: + * @self: a #GtkBoolFilter + * @expression: a #GtkExpression + * + * Sets the expression that the filter uses to + * check if items should be filtered. The expression must have + * a value type of #G_TYPE_BOOLEAN. + */ +void +gtk_bool_filter_set_expression (GtkBoolFilter *self, + GtkExpression *expression) +{ + g_return_if_fail (GTK_IS_BOOL_FILTER (self)); + g_return_if_fail (expression == NULL || gtk_expression_get_value_type (expression) == G_TYPE_BOOLEAN); + + if (self->expression == expression) + return; + + g_clear_pointer (&self->expression, gtk_expression_unref); + if (expression) + self->expression = gtk_expression_ref (expression); + + gtk_filter_changed (GTK_FILTER (self), GTK_FILTER_CHANGE_DIFFERENT); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EXPRESSION]); +} + +/** + * gtk_bool_filter_get_invert: + * @self: a #GtkBoolFilter + * + * Returns whether the filter inverts the expression. + * + * Returns: %TRUE if the filter inverts + */ +gboolean +gtk_bool_filter_get_invert (GtkBoolFilter *self) +{ + g_return_val_if_fail (GTK_IS_BOOL_FILTER (self), TRUE); + + return self->invert; +} + +/** + * gtk_bool_filter_set_invert: + * @self: a #GtkBoolFilter + * @invert: %TRUE to invert + * + * Sets whether the filter should invert the expression. + */ +void +gtk_bool_filter_set_invert (GtkBoolFilter *self, + gboolean invert) +{ + g_return_if_fail (GTK_IS_BOOL_FILTER (self)); + + if (self->invert == invert) + return; + + self->invert = invert; + + gtk_filter_changed (GTK_FILTER (self), GTK_FILTER_CHANGE_DIFFERENT); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_INVERT]); +} + diff --git a/gtk/gtkboolfilter.h b/gtk/gtkboolfilter.h new file mode 100644 index 0000000000..937f74a3e0 --- /dev/null +++ b/gtk/gtkboolfilter.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2020 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_BOOL_FILTER_H__ +#define __GTK_BOOL_FILTER_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_BOOL_FILTER (gtk_bool_filter_get_type ()) +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GtkBoolFilter, gtk_bool_filter, GTK, BOOL_FILTER, GtkFilter) + +GDK_AVAILABLE_IN_ALL +GtkFilter * gtk_bool_filter_new (GtkExpression *expression); + +GDK_AVAILABLE_IN_ALL +GtkExpression * gtk_bool_filter_get_expression (GtkBoolFilter *self); +GDK_AVAILABLE_IN_ALL +void gtk_bool_filter_set_expression (GtkBoolFilter *self, + GtkExpression *expression); +GDK_AVAILABLE_IN_ALL +gboolean gtk_bool_filter_get_invert (GtkBoolFilter *self); +GDK_AVAILABLE_IN_ALL +void gtk_bool_filter_set_invert (GtkBoolFilter *self, + gboolean invert); + + +G_END_DECLS + +#endif /* __GTK_BOOL_FILTER_H__ */ diff --git a/gtk/meson.build b/gtk/meson.build index a30f2e9d89..2e68135422 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -27,6 +27,7 @@ gtk_private_sources = files([ 'gtkbookmarksmanager.c', 'gtkbuilder-menus.c', 'gtkbuilderprecompile.c', + 'gtkbuiltinicon.c', 'gtkcellareaboxcontext.c', 'gtkcoloreditor.c', 'gtkcolorplane.c', @@ -108,7 +109,6 @@ gtk_private_sources = files([ 'gtkgizmo.c', 'gtkgladecatalog.c', 'gtkhsla.c', - 'gtkbuiltinicon.c', 'gtkiconcache.c', 'tools/gtkiconcachevalidator.c', 'gtkiconhelper.c', @@ -164,6 +164,7 @@ gtk_public_sources = files([ 'gtkassistant.c', 'gtkbinlayout.c', 'gtkbitset.c', + 'gtkboolfilter.c', 'gtkbookmarklist.c', 'gtkborder.c', 'gtkboxlayout.c', @@ -452,6 +453,7 @@ gtk_public_headers = files([ 'gtkbinlayout.h', 'gtkbitset.h', 'gtkbookmarklist.h', + 'gtkboolfilter.h', 'gtkborder.h', 'gtkbox.h', 'gtkboxlayout.h', diff --git a/testsuite/gtk/filter.c b/testsuite/gtk/filter.c index 22498bf20c..76636cc1df 100644 --- a/testsuite/gtk/filter.c +++ b/testsuite/gtk/filter.c @@ -309,6 +309,50 @@ test_string_properties (void) g_object_unref (filter); } +static void +test_bool_simple (void) +{ + GtkFilterListModel *model; + GtkExpression *expr; + GtkFilter *filter; + + filter = gtk_bool_filter_new ( + gtk_cclosure_expression_new (G_TYPE_BOOLEAN, + NULL, + 0, NULL, + G_CALLBACK (divisible_by), + GUINT_TO_POINTER (3), NULL)); + model = new_model (20, filter); + assert_model (model, "3 6 9 12 15 18"); + + gtk_bool_filter_set_invert (GTK_BOOL_FILTER (filter), TRUE); + assert_model (model, "1 2 4 5 7 8 10 11 13 14 16 17 19 20"); + + gtk_bool_filter_set_invert (GTK_BOOL_FILTER (filter), FALSE); + assert_model (model, "3 6 9 12 15 18"); + + expr = gtk_cclosure_expression_new (G_TYPE_BOOLEAN, + NULL, + 0, NULL, + G_CALLBACK (divisible_by), + GUINT_TO_POINTER (5), NULL); + gtk_bool_filter_set_expression (GTK_BOOL_FILTER (filter), expr); + gtk_expression_unref (expr); + assert_model (model, "5 10 15 20"); + + gtk_bool_filter_set_invert (GTK_BOOL_FILTER (filter), TRUE); + assert_model (model, "1 2 3 4 6 7 8 9 11 12 13 14 16 17 18 19"); + + gtk_bool_filter_set_expression (GTK_BOOL_FILTER (filter), NULL); + assert_model (model, ""); + + gtk_bool_filter_set_invert (GTK_BOOL_FILTER (filter), FALSE); + assert_model (model, ""); + + g_object_unref (filter); + g_object_unref (model); +} + static void test_every_dispose (void) { @@ -343,6 +387,7 @@ main (int argc, char *argv[]) g_test_add_func ("/filter/any/simple", test_any_simple); g_test_add_func ("/filter/string/simple", test_string_simple); g_test_add_func ("/filter/string/properties", test_string_properties); + g_test_add_func ("/filter/bool/simple", test_bool_simple); g_test_add_func ("/filter/every/dispose", test_every_dispose); return g_test_run (); -- 2.30.2